﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using GpxToolsLib;
using System.Collections.ObjectModel;
using Microsoft.Maps.MapControl;
using TrailMap.Utils;
using System.Collections;
using System.Threading;

namespace TrailMap.Controls
{
    public partial class DrawTrack : UserControl
    {

        public delegate void OutputTrackChangedHandler(bool doNotChangeView);
        private readonly Color normalColor = Color.FromArgb(0xFF, 0xE5, 0x69, 0x10);
        private readonly Color highlighColor = Colors.Blue;

        private string lastLoadedCreator = "Home Brew";
        private string lastLoadedVersion = "1.1";

        private MainPage m_mainpage = null;

        private TrackPoints onEdit = null;

        public TrackPoints CurrentEditTrack
        {
            get { return onEdit; }
            set { onEdit = value; }
        }

        public List<WaypointPin> CurrentWayPoints
        {
            get
            {
                List<WaypointPin> retVal = new List<WaypointPin>();

                foreach (wpt item in outputWaypoints)
                {
                    Location tempLoc = new Location(Double.Parse(item.lat), Double.Parse(item.lon));

                    WaypointPin pushPin = new WaypointPin();

                    pushPin.Width = 90;
                    pushPin.Height = 36;

                    pushPin.DataContext = item;

                    pushPin.Location = tempLoc;

                    pushPin.MouseEnter += new MouseEventHandler(waypoint_MouseEnter);
                    pushPin.MouseLeave += new MouseEventHandler(waypoint_MouseLeave);
                    pushPin.MouseRightButtonDown += new MouseButtonEventHandler(waypoint_MouseRightButtonDown);
                    pushPin.MouseLeftButtonDown += new MouseButtonEventHandler(waypoint_MouseLeftButtonDown);

                    retVal.Add(pushPin);
                }

                return retVal;
            }
        }


        public void AddWayPoints(Location loc)
        {
            wpt tempWpt = new wpt();

            tempWpt.name = string.Format("waypoint{0}",outputWaypoints.Count + 1);

            tempWpt.lon = loc.Longitude.ToString();
            tempWpt.lat = loc.Latitude.ToString();

            outputWaypoints.Add(tempWpt);
        }

        public event OutputTrackChangedHandler OutputTrackChanged;

        ObservableCollection<trk> inputTrks = new ObservableCollection<trk>();
        ObservableCollection<trk> outputTrks = new ObservableCollection<trk>();
        ObservableCollection<wpt> inputWaypoints = new ObservableCollection<wpt>();
        ObservableCollection<wpt> outputWaypoints = new ObservableCollection<wpt>();

        List<TrackPoints> lastRender = null;

        public List<TrackPoints> OutputTrks
        {
            get 
            {
                lastRender = new List<TrackPoints>();

                foreach (trk item in outputTrks)
                {
                    MapPolyline tempData = new MapPolyline();

                    if (onEdit != null && item == onEdit.Track)
                    {
                        tempData.Stroke = new SolidColorBrush(highlighColor);
                    }
                    else
                    {
                        tempData.Stroke = new SolidColorBrush(normalColor);
                    }
                    tempData.StrokeThickness = 5;
                    tempData.Opacity = .75;

                    LocationCollection locations = new LocationCollection();

                    bool isEditTrack = onEdit != null && item == onEdit.Track;

                    ObservableCollection<Pushpin> currentPins = new ObservableCollection<Pushpin>();

                    foreach (trkpt locItem in item.trkseg)
                    {
                        Location tempLoc = new Location(Double.Parse(locItem.lat), Double.Parse(locItem.lon), locItem.Elevation);
                        locations.Add(tempLoc);

                        if (isEditTrack)
                        {
                            Pushpin pushPin = new Pushpin();

                            pushPin.Location = tempLoc;

                            pushPin.MouseEnter += new MouseEventHandler(pushPin_MouseEnter);
                            pushPin.MouseLeave += new MouseEventHandler(pushPin_MouseLeave);
                            pushPin.MouseRightButtonDown += new MouseButtonEventHandler(pushPin_MouseRightButtonDown);
                            pushPin.MouseLeftButtonDown += new MouseButtonEventHandler(pushPin_MouseLeftButtonDown);

                            currentPins.Add(pushPin);
                        }
                    }

                    tempData.Locations = locations;

                    TrackPoints resultData = new TrackPoints(tempData, currentPins, item);

                    if (isEditTrack)
                    {
                        onEdit = resultData;
                    }

                    lastRender.Add(resultData);
                }

                return lastRender; 
            }
        }

        #region Output Track List box context menu event handlers
        private void EditMenuItem_Click(object sender, RoutedEventArgs e)
        {

            trk tempData = listOfOutputTracks.SelectedItem as trk;

            if (tempData == null && outputTrks.Count > 0)
            {
                tempData = outputTrks[0];
            }

            if (tempData != null)
            {
                onEdit = (from trackLocal in lastRender where trackLocal.Track == tempData select trackLocal).First<TrackPoints>();
            }

            if (onEdit == null)
            {
                MessageBox.Show("please select any one track in the outputtrack list and click edit");
            }

            UpdateOutputTracksMenuItemVisiblity();

            if (OutputTrackChanged != null && this.Visibility == System.Windows.Visibility.Visible)
            {
                OutputTrackChanged(true);
            }
        }

        private void UpdateOutputTracksMenuItemVisiblity()
        {
            if (onEdit != null)
            {
                trackPointEditMenuItem.Visibility = System.Windows.Visibility.Collapsed;
                trackPointStopEditMenuItem.Visibility = System.Windows.Visibility.Visible;
            }
            else
            {
                trackPointEditMenuItem.Visibility = System.Windows.Visibility.Visible;
                trackPointStopEditMenuItem.Visibility = System.Windows.Visibility.Collapsed;
            }
        }

        private void StopEditMenuItem_Click(object sender, RoutedEventArgs e)
        {
            StopEditTrack();
        }

        private void StopEditTrack()
        {
            onEdit = null;

            UpdateOutputTracksMenuItemVisiblity();

            if (OutputTrackChanged != null && this.Visibility == System.Windows.Visibility.Visible)
            {
                OutputTrackChanged(true);
            }
        }
        private void trackPlotThisMenuItem_Click(object sender, RoutedEventArgs e)
        {
            List<trkpt> tempData = new List<trkpt>();

            if (listOfOutputTracks.SelectedItem != null)
            {
                foreach (object item in listOfOutputTracks.SelectedItems)
                {
                    trk trkData = item as trk;

                    tempData.AddRange(trkData.trkseg);
                }
            }
            else
            {
                if (outputTrks.Count > 0)
                {

                    trk trkData = outputTrks[0];

                    tempData.AddRange(trkData.trkseg);

                }
            }

            if (tempData.Count > 0)
            {
                m_mainpage.elePlot.Visibility = System.Windows.Visibility.Visible;

                var data = from trkPt in tempData orderby trkPt.time select trkPt;

                m_mainpage.elePlot.PlotTrack(data.ToList<trkpt>());
            }
        }
        #endregion

        #region Waypoint PushPin methods
        void waypoint_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            WaypointPin pushPin = sender as WaypointPin;
            wpt tempwpt = pushPin.DataContext as wpt;

            EditWaypoint wayPoint = new EditWaypoint();

            wayPoint.DataContext = tempwpt;

            wayPoint.Show();
        }

        void waypoint_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            WaypointPin pushPin = sender as WaypointPin;
            wpt tempwpt = pushPin.DataContext as wpt;

            outputWaypoints.Remove(tempwpt);
            inputWaypoints.Add(tempwpt);

            if (OutputTrackChanged != null && this.Visibility == System.Windows.Visibility.Visible)
            {
                OutputTrackChanged(true);
            }

            e.Handled = true;
        }

        void waypoint_MouseLeave(object sender, MouseEventArgs e)
        {
            WaypointPin pushPin = sender as WaypointPin;

            if (pushPin != null)
            {
                pushPin.Background = new SolidColorBrush(normalColor);
            }
        }

        void waypoint_MouseEnter(object sender, MouseEventArgs e)
        {
            WaypointPin pushPin = sender as WaypointPin;

            if (pushPin != null)
            {
                pushPin.Background = new SolidColorBrush(highlighColor);
            }

        }
        #endregion

        #region Track PushPin methods
        void pushPin_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {

        }

        void pushPin_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            foreach (TrackPoints item in lastRender)
            {
                if (item.PushPins.Count > 0)
                {
                    int index = item.PushPins.IndexOf(sender as Pushpin);

                    if (index != -1)
                    {
                        item.Track.trkseg.RemoveAt(index);
                    }
                }
            }

            if (OutputTrackChanged != null && this.Visibility == System.Windows.Visibility.Visible)
            {
                OutputTrackChanged(true);
            }

            e.Handled = true;
        }

        void pushPin_MouseLeave(object sender, MouseEventArgs e)
        {
            Pushpin pushPin = sender as Pushpin;

            if (pushPin != null)
            {
                pushPin.Background = new SolidColorBrush(normalColor);
            }
        }

        void pushPin_MouseEnter(object sender, MouseEventArgs e)
        {
            Pushpin pushPin = sender as Pushpin;

            if (pushPin != null)
            {
                pushPin.Background = new SolidColorBrush(highlighColor);
            }

        }  
        #endregion

        #region ctor
        public DrawTrack()
        {
            InitializeComponent();

            listOfOutputTracks.ItemsSource = outputTrks;
            listOfOutputWaypoints.ItemsSource = outputWaypoints;

            outputTrks.CollectionChanged += outputTrks_CollectionChanged;
            outputWaypoints.CollectionChanged += outputTrks_CollectionChanged;
        }

        public void InitializeDrawTrack(MainPage mainPage)
        {
            m_mainpage = mainPage;
        } 
        #endregion

        #region Global Events
        void outputTrks_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (OutputTrackChanged != null && this.Visibility == System.Windows.Visibility.Visible)
            {
                OutputTrackChanged(false);
            }
        }

        private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (this.CurrentEditTrack != null && mainTab.SelectedIndex != 0)
            {
                StopEditTrack();
            }
        } 
        #endregion

        #region Button Event handlers
        private void bttnInputAdd_Click(object sender, RoutedEventArgs e)
        {
            gpx gpxFile = GpxFiles.LoadGpx();

            lastLoadedCreator = gpxFile.creator;
            lastLoadedVersion = gpxFile.version;

            if (gpxFile != null && gpxFile.trk.Count > 0)
            {
                foreach (trk item in gpxFile.trk)
                {
                    inputTrks.Add(item);
                }

                //TODO:implement code to Add the Waypoints
            }

            listOfInputTracks.ItemsSource = inputTrks;
        }

        private void bttnInputRemove_Click(object sender, RoutedEventArgs e)
        {
            if (listOfInputTracks.SelectedItem != null)
            {
                inputTrks.Remove(listOfInputTracks.SelectedItem as trk);
            }
        }

        private void bttnOutputNew_Click(object sender, RoutedEventArgs e)
        {
            trk trkTemp = new trk();
            trkTemp.name = string.Format("Track{0}", outputTrks.Count);

            outputTrks.Add(trkTemp);
        }

        private void bttnOutputRemove_Click(object sender, RoutedEventArgs e)
        {
            if (listOfOutputTracks.SelectedItem != null)
            {
                outputTrks.Remove(listOfOutputTracks.SelectedItem as trk);
            }
        }

        private void bttnInputWaypointsAdd_Click(object sender, RoutedEventArgs e)
        {
            gpx gpxFile = GpxFiles.LoadGpx();

            lastLoadedCreator = gpxFile.creator;
            lastLoadedVersion = gpxFile.version;

            if (gpxFile != null && gpxFile.trk.Count > 0)
            {

                foreach (wpt item in gpxFile.wpt)
                {
                    inputWaypoints.Add(item);
                }

                //TODO:implement code to Add the Waypoints
            }

            listOfInputWaypoints.ItemsSource = inputWaypoints;
        }

        private void bttnMoveTrackLeft_Click(object sender, RoutedEventArgs e)
        {
            List<trk> tempVal = new List<trk>();
            if (listOfInputTracks.SelectedItems != null)
            {
                foreach (object item in listOfInputTracks.SelectedItems)
                {
                    outputTrks.Add(item as trk);
                    tempVal.Add(item as trk);
                }

                foreach (trk item in tempVal)
                {
                    inputTrks.Remove(item);
                }
            }

        }

        private void bttnMoveTrackRight_Click(object sender, RoutedEventArgs e)
        {
            List<trk> tempVal = new List<trk>();
            if (listOfOutputTracks.SelectedItems != null)
            {
                foreach (object item in listOfOutputTracks.SelectedItems)
                {
                    inputTrks.Add(item as trk);
                    tempVal.Add(item as trk);
                }

                foreach (trk item in tempVal)
                {
                    outputTrks.Remove(item);
                }
            }
        }

        private void bttnMoveWaypointLeft_Click(object sender, RoutedEventArgs e)
        {
            List<wpt> tempVal = new List<wpt>();

            if (listOfInputWaypoints.SelectedItems != null)
            {
                foreach (object item in listOfInputWaypoints.SelectedItems)
                {
                    outputWaypoints.Add(item as wpt);
                    tempVal.Add(item as wpt);
                }

                foreach (wpt item in tempVal)
                {
                    inputWaypoints.Remove(item);
                }
            }

        }

        private void bttnMoveWaypointRight_Click(object sender, RoutedEventArgs e)
        {
            List<wpt> tempVal = new List<wpt>();

            if (listOfOutputWaypoints.SelectedItems != null)
            {
                foreach (object item in listOfOutputWaypoints.SelectedItems)
                {
                    inputWaypoints.Add(item as wpt);
                    tempVal.Add(item as wpt);
                }

                foreach (wpt item in tempVal)
                {
                    outputWaypoints.Remove(item);
                }
            }
        }

        private void bttnLoadGpxFile_Click(object sender, RoutedEventArgs e)
        {
            gpx gpxFile = GpxFiles.LoadGpx();

            lastLoadedCreator = gpxFile.creator;
            lastLoadedVersion = gpxFile.version;

            if (gpxFile != null && gpxFile.trk.Count > 0)
            {

                foreach (trk item in gpxFile.trk)
                {
                    inputTrks.Add(item);
                }

                foreach (wpt item in gpxFile.wpt)
                {
                    inputWaypoints.Add(item);
                }

            }

            listOfInputWaypoints.ItemsSource = inputWaypoints;
            listOfInputTracks.ItemsSource = inputTrks;
        }

        private void bttnSaveGPXFile_Click(object sender, RoutedEventArgs e)
        {
            gpx gpxFile = new gpx();

            gpxFile.creator = lastLoadedCreator;
            gpxFile.version = lastLoadedVersion;

            foreach (trk item in outputTrks)
            {
                gpxFile.trk.Add(item);
            }

            foreach (wpt item in outputWaypoints)
            {
                gpxFile.wpt.Add(item);
            }

            GpxFiles.SaveGpx(gpxFile);
        }

        private void bttnPlotTrackElevation_Click(object sender, RoutedEventArgs e)
        {
            List<trkpt> tempData = new List<trkpt>();

            foreach (trk item in inputTrks)
            {
                tempData.AddRange(item.trkseg);
            }

            if (tempData.Count > 0)
            {
                m_mainpage.elePlot.Visibility = System.Windows.Visibility.Visible;

                var data = from trkPt in tempData orderby trkPt.time select trkPt;
                m_mainpage.elePlot.ClearPlot();
                m_mainpage.elePlot.PlotTrack(data.ToList<trkpt>());
            }

            m_mainpage.elePlot.Visibility = System.Windows.Visibility.Visible;
        }

        private void bttnPlotWaypointElevation_Click(object sender, RoutedEventArgs e)
        {
            List<trkpt> tempData = new List<trkpt>();

            foreach (wpt item in inputWaypoints)
            {
                trkpt trkpt = new GpxToolsLib.trkpt();

                trkpt.ele = item.ele;
                trkpt.time = item.time;
                trkpt.lat = item.lat;
                trkpt.lon = item.lon;

                tempData.Add(trkpt);
            }

            if (tempData.Count > 0)
            {
                m_mainpage.elePlot.Visibility = System.Windows.Visibility.Visible;

                var data = from trkPt in tempData orderby trkPt.time select trkPt;

                m_mainpage.elePlot.ClearPlot();

                m_mainpage.elePlot.PlotTrack(data.ToList<trkpt>());
            }

            m_mainpage.elePlot.Visibility = System.Windows.Visibility.Visible;
        } 
        #endregion

        private void bttnGetWaypointElevation_Click(object sender, RoutedEventArgs e)
        {
            

        }

        
    }
}
